/*
 * Copyright (C) 2010-2015 FBReader.ORG Limited <contact@fbreader.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.geometerplus.zlibrary.ui.android.image;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import org.geometerplus.zlibrary.core.image.ZLFileImage;
import org.geometerplus.zlibrary.core.image.ZLImageData;
import org.geometerplus.zlibrary.core.image.ZLImageManager;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;

public class ImageViewActivity extends Activity {
    public static final String URL_KEY = "fbreader.imageview.url";
    public static final String BACKGROUND_COLOR_KEY = "fbreader.imageview.background";

    private Bitmap myBitmap;
    private ZLColor myBgColor;

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        final ZLAndroidLibrary library = (ZLAndroidLibrary) ZLAndroidLibrary.Instance();
        final boolean showStatusBar = library.ShowStatusBarOption.getValue();
        getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN,
                showStatusBar ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN
        );


        setContentView(new ImageView());

        final Intent intent = getIntent();

        myBgColor = new ZLColor(0xff, 0xfff, 0xff);

        final String url = intent.getStringExtra(URL_KEY);
        final String prefix = ZLFileImage.SCHEME + "://";
        if (url != null && url.startsWith(prefix)) {
            final ZLFileImage image = ZLFileImage.byUrlPath(url.substring(prefix.length()));
            if (image == null) {

                finish();
            }
            try {
                final ZLImageData imageData = ZLImageManager.Instance().getImageData(image);
                myBitmap = ((ZLAndroidImageData) imageData).getFullSizeBitmap();
            } catch (Exception e) {
                // TODO: error message (?)
                e.printStackTrace();
                finish();
            }
        } else {
            // TODO: error message (?)
            finish();
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (myBitmap != null) {
            myBitmap.recycle();
        }
        myBitmap = null;
    }

    private class ImageView extends View {
        private final Paint myPaint = new Paint();

        private volatile int myDx = 0;
        private volatile int myDy = 0;
        private volatile float myZoomFactor = 1.0f;

        ImageView() {
            super(ImageViewActivity.this);
        }

        @Override
        protected void onDraw(final Canvas canvas) {
            myPaint.setColor(ZLAndroidColorUtil.rgb(myBgColor));
            final int w = getWidth();
            final int h = getHeight();
            canvas.drawRect(0, 0, w, h, myPaint);
            if (myBitmap == null || myBitmap.isRecycled()) {
                return;
            }

            final int bw = (int) (myBitmap.getWidth() * myZoomFactor);
            final int bh = (int) (myBitmap.getHeight() * myZoomFactor);

            final Rect src = new Rect(0, 0, (int) (w / myZoomFactor), (int) (h / myZoomFactor));
            final Rect dst = new Rect(0, 0, w, h);
            if (bw <= w) {
                src.left = 0;
                src.right = myBitmap.getWidth();
                dst.left = (w - bw) / 2;
                dst.right = dst.left + bw;
            } else {
                final int bWidth = myBitmap.getWidth();
                final int pWidth = (int) (w / myZoomFactor);
                src.left = Math.min(bWidth - pWidth, Math.max((bWidth - pWidth) / 2 - myDx, 0));
                src.right += src.left;
            }
            if (bh <= h) {
                src.top = 0;
                src.bottom = myBitmap.getHeight();
                dst.top = (h - bh) / 2;
                dst.bottom = dst.top + bh;
            } else {
                final int bHeight = myBitmap.getHeight();
                final int pHeight = (int) (h / myZoomFactor);
                src.top = Math.min(bHeight - pHeight, Math.max((bHeight - pHeight) / 2 - myDy, 0));
                src.bottom += src.top;
            }
            canvas.drawBitmap(myBitmap, src, dst, myPaint);
        }

        private void shift(int dx, int dy) {
            if (myBitmap == null || myBitmap.isRecycled()) {
                return;
            }

            final int w = (int) (getWidth() / myZoomFactor);
            final int h = (int) (getHeight() / myZoomFactor);
            final int bw = myBitmap.getWidth();
            final int bh = myBitmap.getHeight();

            final int newDx, newDy;

            if (w < bw) {
                final int delta = (bw - w) / 2;
                newDx = Math.max(-delta, Math.min(delta, myDx + dx));
            } else {
                newDx = myDx;
            }
            if (h < bh) {
                final int delta = (bh - h) / 2;
                newDy = Math.max(-delta, Math.min(delta, myDy + dy));
            } else {
                newDy = myDy;
            }

            if (newDx != myDx || newDy != myDy) {
                myDx = newDx;
                myDy = newDy;
                postInvalidate();
            }
        }

        private boolean myMotionControl;
        private int mySavedX;
        private int mySavedY;

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getPointerCount()) {
                case 1:
                    return onSingleTouchEvent(event);
                case 2:
                    return onDoubleTouchEvent(event);
                default:
                    return false;
            }
        }

        private boolean onSingleTouchEvent(MotionEvent event) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    myMotionControl = false;
                    break;
                case MotionEvent.ACTION_DOWN:
                    myMotionControl = true;
                    mySavedX = x;
                    mySavedY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (myMotionControl) {
                        shift(
                                (int) ((x - mySavedX) / myZoomFactor),
                                (int) ((y - mySavedY) / myZoomFactor)
                        );
                    }
                    myMotionControl = true;
                    mySavedX = x;
                    mySavedY = y;
                    break;
            }
            return true;
        }

        private float myStartPinchDistance2 = -1;
        private float myStartZoomFactor;

        private boolean onDoubleTouchEvent(MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_POINTER_UP:
                    myStartPinchDistance2 = -1;
                    break;
                case MotionEvent.ACTION_POINTER_DOWN: {
                    final float diffX = event.getX(0) - event.getX(1);
                    final float diffY = event.getY(0) - event.getY(1);
                    myStartPinchDistance2 = Math.max(diffX * diffX + diffY * diffY, 10f);
                    myStartZoomFactor = myZoomFactor;
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    final float diffX = event.getX(0) - event.getX(1);
                    final float diffY = event.getY(0) - event.getY(1);
                    final float distance2 = Math.max(diffX * diffX + diffY * diffY, 10f);
                    if (myStartPinchDistance2 < 0) {
                        myStartPinchDistance2 = distance2;
                        myStartZoomFactor = myZoomFactor;
                    } else {
                        myZoomFactor = (float) (myStartZoomFactor * Math.sqrt(distance2 / myStartPinchDistance2));
                        postInvalidate();
                    }
                }
                break;
            }
            return true;
        }
    }
}
